{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 12.1\n",
    "CTA全称是Commodity Trading Advisor，即“商品交易顾问”，是由NFA（美国全国期货协会）认定的，在CFTC（商品期货交易委员会）注册，并接受监督的投资顾问。\n",
    "\n",
    "CTA一般是指通过为客户提供期权、期货方面的交易建议，或者直接通过受管理的期货账户参与实际交易，来获得收益的机构或个人。\n",
    "\n",
    "实际上，目前国内的CTA策略大都是基于量价的趋势跟踪策略。\n",
    "\n",
    "无论是商品期货、金融期货，还是股票，外汇，只要是有历史公开量价的二级市场，都可以成为CTA策略运作的市场。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 12.2 技术指标\n",
    "所谓技术指标，就是价格、成交量、持仓量的数学组合。\n",
    "\n",
    "大体上，技术指标可以分为三种类型：\n",
    "- 趋势型：趋势指标可以用于描述并捕捉趋势行情，适合趋势跟踪策略\n",
    "- 超买超卖型：与趋势型刚好相反，此类指标可用于描述并捕捉趋势行情的终结，即反转状态，目标是为了识别震荡和短期的头部底部\n",
    "- 能量型：能量型的指标是指从成交量的角度考察价格变动的力量。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 12.3 主力合约的换月问题\n",
    "期货合约会到期。\n",
    "\n",
    "若要进行较长历史的回测，使用的数据是由多个合约拼接而成的，也就是所谓的主力连续合约。\n",
    "\n",
    "在换月时，会产生“假跳空”问题。\n",
    "\n",
    "由于不同合约经常会存在一个较大的价差，因此表现在连续合约上，就会产生一个大的跳空。这个就是“假跳空”。\n",
    "\n",
    "最大的影响就是技术指标的计算会失真。\n",
    "\n",
    "在进行回测的时候，需要处理“假跳空”的问题。一般采用如下三种方法：\n",
    "- 使用期货的合成指数来进行回测\n",
    "- 对跳空进行复权处理\n",
    "- 不使用主力合约，而是使用单独月份的合约来进行分析"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 12.4 用python实现复权\n",
    "#### 12.4.1 加减复权\n",
    "假设每次换月的时候，换月前的收盘价和换月后的开盘价就是换月导致的“假跳空”价差。\n",
    "\n",
    "基于这个简单的假设，可以进行加减复权：\n",
    "- 初始化复权因子为0\n",
    "- 每次换月后，将“假跳空”累加的复权因子上，算出所有的复权因子\n",
    "- 统一将所有的价格都减去复权因子\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def adjust_price_sum(df):\n",
    "    \"\"\"\n",
    "    加减复权\n",
    "    参数：\n",
    "        df：原始的OHLC数据，列名一次为：date，trade_hiscode, open, high, low, close，其中，trade_hiscode代表了合约的代码，因为存在换月问题，因此这个代码是会变动的\n",
    "    返回值：\n",
    "        df：进行复权处理后的数据，OHLC所有的价格数据都要进行复权，并返回对应的复权因子adj_factor\n",
    "\n",
    "    \"\"\"\n",
    "    \n",
    "    # 生成前一天的收盘价，以便于向量化计算复权价格\n",
    "    df['close_pre'] = df.close.shift(1)\n",
    "    \n",
    "    # 判断是否换月\n",
    "    df['roll_over'] = (df.trade_hiscode != df.trade_hiscode.shift(1)) & (~df.trade_hiscode.shift(1).isnull())\n",
    "    \n",
    "    # 初始化复权因子\n",
    "    df['adj_factor'] = 0\n",
    "    \n",
    "    # 计算因为换月问题导致的复权因子变化\n",
    "    df.loc[df.roll_over, 'adj_factor'] = df.loc[df.roll_over, 'close_pre'] - df.loc[df.roll_over, 'open']\n",
    "    \n",
    "    # 累积所有的复权因子的变化，得到最终的复权因子\n",
    "    df['adj_factor_cum'] = df.adj_factor.cumsum()\n",
    "    \n",
    "    # 对OHLC的四个数据都进行复权\n",
    "    df['open'] = df.open + df.adj_factor_cum\n",
    "    df['high'] = df.high + df.adj_factor_cum\n",
    "    df['low'] = df.low + df.adj_factor_cum\n",
    "    df['close'] = df.close + df.adj_factor_cum\n",
    "    \n",
    "    # 删除中间数据\n",
    "    del df['close_pre']\n",
    "    del df['roll_over']\n",
    "    del df['adj_factor']\n",
    "    del df['adj_factor_cum']\n",
    "    \n",
    "    df = df.dropna()\n",
    "    \n",
    "    return df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 12.4.2 乘除复权\n",
    "- 初始化复权因子为1\n",
    "- 每次换月后，将“假跳空”累乘到复权因子上，算出所有的复权因子\n",
    "- 统一讲所有的价格都乘以复权因子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def adjust_price_prod(df):\n",
    "    \"\"\"\n",
    "    乘除复权\n",
    "    参数：\n",
    "        df：原始的OHLC数据，列名一次为：date，trade_hiscode, open, high, low, close，其中，trade_hiscode代表了合约的代码，因为存在换月问题，因此这个代码是会变动的\n",
    "    返回值：\n",
    "        df：进行复权处理后的数据，OHLC所有的价格数据都要进行复权，并返回对应的复权因子adj_factor\n",
    "\n",
    "    \"\"\"\n",
    "    \n",
    "    # 生成前一天的收盘价，以便于向量化计算复权价格\n",
    "    df['close_pre'] = df.close.shift(1)\n",
    "    \n",
    "    # 判断是否换月\n",
    "    df['roll_over'] = (df.trade_hiscode != df.trade_hiscode.shift(1)) & (~df.trade_hiscode.shift(1).isnull())\n",
    "    \n",
    "    # 初始化复权因子\n",
    "    df['adj_factor'] = 1\n",
    "    \n",
    "    # 计算因为换月问题导致的复权因子变化\n",
    "    df.loc[df.roll_over, 'adj_factor'] = df.loc[df.roll_over, 'close_pre'] / df.loc[df.roll_over, 'open']\n",
    "    \n",
    "    # 累积所有的复权因子的变化，得到最终的复权因子\n",
    "    df['adj_factor_cum'] = df.adj_factor.cumprod()\n",
    "    \n",
    "    # 对OHLC的四个数据都进行复权\n",
    "    df['open'] = round(df.open * df.adj_factor_cum, 2)\n",
    "    df['high'] = round(df.high * df.adj_factor_cum, 2)\n",
    "    df['low'] = round(df.low * df.adj_factor_cum, 2)\n",
    "    df['close'] = round(df.close * df.adj_factor_cum, 2)\n",
    "    \n",
    "    # 删除中间数据\n",
    "    del df['close_pre']\n",
    "    del df['roll_over']\n",
    "    del df['adj_factor']\n",
    "    del df['adj_factor_cum']\n",
    "    \n",
    "    df = df.dropna()\n",
    "    \n",
    "    return df"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
